package it.antonio.ilpizze.db.index.btree.file;

import it.antonio.ilpizze.db.index.btree.IntermediateNode;
import it.antonio.ilpizze.db.index.btree.Node;
import it.antonio.ilpizze.db.index.utils.FPerst;
import it.antonio.ilpizze.db.index.utils.FilePointer;
import it.antonio.ilpizze.db.io.IOFile;
import it.antonio.ilpizze.func.ComputeIterator;

import java.util.Iterator;

public class FileIntermediateNode<K extends Comparable<K>, V> implements IntermediateNode<K, V>, FilePointer, FileBTreeConstants {

	/*
	 *  type|isLeaf|key|size|child_positions
	 */
	
	private IOFile file;
	
	private long position;
	private int degree;
	private FPerst<K> keyPersister;
	private FPerst<V> valuePersister;
	
	public FileIntermediateNode(IOFile file, long position, int degree, FPerst<K> keyPersister, FPerst<V> valuePersister) {
		this.file = file;
		this.position = position;
		this.degree = degree;
		this.keyPersister = keyPersister;
		this.valuePersister = valuePersister;
		
		file.position(position);
		int typeCheck = file.readInt();
		if(typeCheck != INTER_NODE){
			throw new IllegalStateException("Invalid InterNode");
		}
	}

	
	@Override
	public K key() {
			file.position(position + SIZE_INT + SIZE_BOOLEAN);
			K key = keyPersister.read(file);
			return key;
		
	}

	@Override
	public boolean isLeaf() {
		return false;
	}

	@Override
	public boolean isFull() {
		return size() >= degree;
	}

	@Override
	public boolean isEmpty() {
		return size() == 0;
	}

	@Override
	public int size() {
			file.position(position + SIZE_INT  + SIZE_BOOLEAN + keyPersister.space());
			return file.readInt();	
		
		
	}

	@Override
	public Iterator<Node<K, V>> iterator() {
			file.position(position + SIZE_INT  + SIZE_BOOLEAN + keyPersister.space());
			final int size = file.readInt();
			
			return new ComputeIterator<Node<K,V>>() {
				
				int count = 0;
				
				@Override
				protected Node<K,V> computeNext() {
					if(count == size) return finished();
					
					long childPosition = position + SIZE_INT  + SIZE_BOOLEAN + keyPersister.space() + SIZE_INT + (count * SIZE_LONG);
					count++;
					
					file.position(childPosition);
					long position = file.readLong();
					
					file.position(position + SIZE_INT);
					boolean isLeaf = file.readBoolean();
					if(isLeaf){
						return new FileLeafNode<K, V>(file, position, size, keyPersister, valuePersister);
					} else {
						return new FileIntermediateNode<K, V>(file, position, degree, keyPersister, valuePersister);
					}
					
				}
			};
		
	}

	@Override
	public void key(K key) {
			file.position(position + SIZE_INT  + SIZE_BOOLEAN);
			keyPersister.persist(file, key);
		
	}

	@Override
	public Node<K,V> get(int i) {
		
			long childIndex = position + SIZE_INT  + SIZE_BOOLEAN + keyPersister.space() + SIZE_INT + (i * SIZE_LONG);
			file.position(childIndex);
			long childPosition = file.readLong();
			
			file.position(childPosition + SIZE_INT);
			
			boolean isLeaf = file.readBoolean();
			if(isLeaf){
				return new FileLeafNode<K,V>(file, childPosition, degree, keyPersister, valuePersister);
			} else {
				return new FileIntermediateNode<K, V>(file, childPosition, degree, keyPersister, valuePersister);	
			}
			
	}

	@Override
	public void add(Node<K, V> node) {
		FilePointer fileNode = (FilePointer) node;
		
		file.position(position + SIZE_INT  + SIZE_BOOLEAN + keyPersister.space());
		int size = file.readInt();
			
		file.position(position + SIZE_INT  + SIZE_BOOLEAN + keyPersister.space() + SIZE_INT + (size * SIZE_LONG));
		file.writeLong(fileNode.position());
		
		file.position(position + SIZE_INT  + SIZE_BOOLEAN + keyPersister.space());
		file.writeInt(size + 1);
		
	}

	@Override
	public void replace(int index, Node<K, V> one, Node<K, V> two) {
		FilePointer fileOne = (FilePointer) one;
		FilePointer filetwo = (FilePointer) two;

		
		
		
		
		file.position(position + SIZE_INT  + SIZE_BOOLEAN + keyPersister.space());
		int size = file.readInt();

		for (int i = size; i > index; i--) {
			file.position(position + SIZE_INT  + SIZE_BOOLEAN + keyPersister.space() + SIZE_INT + ((i - 1) * SIZE_LONG));
			long childPos = file.readLong();
			file.writeLong(childPos);
		}
		file.position(position + SIZE_INT  + SIZE_BOOLEAN + keyPersister.space() + SIZE_INT + (index * SIZE_LONG));
		file.writeLong(fileOne.position());
		file.writeLong(filetwo.position());
		file.position(position + SIZE_INT  + SIZE_BOOLEAN + keyPersister.space());
		
		file.writeInt(size + 1);
	
	}
	
	@Override
	public long position() {
		return position;
	}

	@Override
	public long space() {
		return  SIZE_INT + SIZE_BOOLEAN + keyPersister.space()+ SIZE_INT + (degree * SIZE_LONG);
	}
	
	
}
